iT邦幫忙

2024 iThome 鐵人賽

DAY 29
0

昨天,我們先說明了如何以Flask架設聊天室後端。
今天,我們使用Tailwind CSS和**JavaScript(JS)**來把聊天室的前端完成,而聊天室由上到下分以下幾個部份:

  • 標題列(可更換白天/黑夜主題)
  • 聊天內容視窗
  • 訊息輸入框

標題列(可更換主題)

在標題列部份,我設置了一個主題更換按鈕,可以更換成白天/黑夜主題(預設為白天)。
https://ithelp.ithome.com.tw/upload/images/20241009/201690303dNfRIjzWi.png
而HTML長這個樣子:

<header class="bg-green-500 text-white p-4 shadow-md">
    <div class="container mx-auto flex justify-between items-center">
       <h1 class="text-xl font-bold">DialoGPT Chat</h1>
         <button id="theme-toggle" class="focus:outline-none"> 
            <i class="fas fa-moon"></i>  <!-- 白天/黑夜按鈕 -->
         </button>
    </div>
</header>

我們在button標籤設定了theme-toggleid以撰寫主題更換功能,以下為JS程式碼:

function toggleTheme() {
    isDarkMode = !isDarkMode; // isDarkMode初始為false
    if (isDarkMode) {
        $('body').addClass('bg-gray-900 text-white');
        $('.bg-white').addClass('bg-gray-800').removeClass('bg-white');
        $('#theme-toggle i').removeClass('fa-moon').addClass('fa-sun');
    } else {
        $('body').removeClass('bg-gray-900 text-white');
        $('.bg-gray-800').addClass('bg-white').removeClass('bg-gray-800');
        $('#theme-toggle i').removeClass('fa-sun').addClass('fa-moon');
    }
}

$('#theme-toggle').click(toggleTheme); // 若主題按鈕被點選就更換主題

實際使用則是這樣:

訊息輸入框

HTML的部份沒啥大問題,就是input標籤。
為了存取使用者輸入的訊息,我們需在輸入標籤加上send-button id,
好讓我們用JS將訊息傳遞至後端。

<!-- Input area -->
<div class="bg-white border-t border-gray-200 p-4">
    <div class="container mx-auto flex space-x-2">
        <input type="text" id="user-input" placeholder="Type a message..." class="flex-1 px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-green-500">
        <button id="send-button" class="px-6 py-2 bg-green-500 text-white rounded-full hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
            <i class="fas fa-paper-plane"></i>
        </button>
    </div>
</div>

輸入框的樣式就長這樣子:
https://ithelp.ithome.com.tw/upload/images/20241009/20169030QdVqXFfTUV.png

訊息處理

我們來複習一下昨天的Falsk程式碼,

@app.route('/chat', methods=['POST'])
def chat():
    ...
    response = tokenizer.decode(chat_history_ids[:, bot_input_ids.shape[-1]:][0], skip_special_tokens=True)
    return jsonify({'response': response})

在訊息路由chat()的開頭,我們以@app.route('/chat')來設定GPT的回覆要傳送到哪個路徑,且末尾我們以jsonify()將GPT的回覆包成JSON格式並回傳,方便讓JS去存取和處理訊息。

那在前端就用AJAX/chat接收到的JSON格式訊息處理成文字訊息,
並用addMessage()formatTime()將文字訊息渲染成像LINE一樣的訊息泡泡,最後顯示在訊息視窗上。

function formatTime(date) {
    return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}

function addMessage(sender, message) {
    const time = formatTime(new Date());
    const alignClass = sender === 'You' ? 'justify-end' : 'justify-start';
    const bgColorClass = sender === 'You' ? 'bg-green-500 text-white' : 'bg-gray-200 text-gray-800';
    
    $('#chat-container').append(`
        <div class="flex ${alignClass}">
            <div class="max-w-xs lg:max-w-md">
                <div class="${bgColorClass} rounded-lg px-4 py-2 inline-block">
                    <p class="font-bold">${sender}</p>
                    <p>${message}</p>
                </div>
                <p class="text-xs text-gray-500 mt-1">${time}</p>
            </div>
        </div>
    `);
    $('#chat-container').scrollTop($('#chat-container')[0].scrollHeight);
}

$('#send-button').click(function() {
    var userInput = $('#user-input').val();
    if (userInput.trim() !== '') {
        addMessage('You', userInput);
        $('#user-input').val('');

        $.ajax({
            url: '/chat',
            method: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({message: userInput}),
            success: function(response) {
                addMessage('Bot', response.response);
            }
        });
    }
});

$('#user-input').keypress(function(e) {
    if (e.which == 13) {
        $('#send-button').click();
    }
});

完整網頁程式碼index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DialoGPT Chat</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100 h-screen flex flex-col">
    <!-- Header -->
    <header class="bg-green-500 text-white p-4 shadow-md">
        <div class="container mx-auto flex justify-between items-center">
            <h1 class="text-xl font-bold">DialoGPT Chat</h1>
            <button id="theme-toggle" class="focus:outline-none">
                <i class="fas fa-moon"></i>
            </button>
        </div>
    </header>

    <!-- Chat container -->
    <div id="chat-container" class="flex-1 overflow-y-auto p-4 space-y-4"></div>

    <!-- Input area -->
    <div class="bg-white border-t border-gray-200 p-4">
        <div class="container mx-auto flex space-x-2">
            <input type="text" id="user-input" placeholder="Type a message..." class="flex-1 px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-green-500">
            <button id="send-button" class="px-6 py-2 bg-green-500 text-white rounded-full hover:bg-green-600 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
                <i class="fas fa-paper-plane"></i>
            </button>
        </div>
    </div>

    <script>
        $(document).ready(function() {
            let isDarkMode = false;

            function toggleTheme() {
                isDarkMode = !isDarkMode;
                if (isDarkMode) {
                    $('body').addClass('bg-gray-900 text-white');
                    $('.bg-white').addClass('bg-gray-800').removeClass('bg-white');
                    $('#theme-toggle i').removeClass('fa-moon').addClass('fa-sun');
                } else {
                    $('body').removeClass('bg-gray-900 text-white');
                    $('.bg-gray-800').addClass('bg-white').removeClass('bg-gray-800');
                    $('#theme-toggle i').removeClass('fa-sun').addClass('fa-moon');
                }
            }

            $('#theme-toggle').click(toggleTheme);

            function formatTime(date) {
                return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
            }

            function addMessage(sender, message) {
                const time = formatTime(new Date());
                const alignClass = sender === 'You' ? 'justify-end' : 'justify-start';
                const bgColorClass = sender === 'You' ? 'bg-green-500 text-white' : 'bg-gray-200 text-gray-800';
                
                $('#chat-container').append(`
                    <div class="flex ${alignClass}">
                        <div class="max-w-xs lg:max-w-md">
                            <div class="${bgColorClass} rounded-lg px-4 py-2 inline-block">
                                <p class="font-bold">${sender}</p>
                                <p>${message}</p>
                            </div>
                            <p class="text-xs text-gray-500 mt-1">${time}</p>
                        </div>
                    </div>
                `);
                $('#chat-container').scrollTop($('#chat-container')[0].scrollHeight);
            }

            $('#send-button').click(function() {
                var userInput = $('#user-input').val();
                if (userInput.trim() !== '') {
                    addMessage('You', userInput);
                    $('#user-input').val('');

                    $.ajax({
                        url: '/chat',
                        method: 'POST',
                        contentType: 'application/json',
                        data: JSON.stringify({message: userInput}),
                        success: function(response) {
                            addMessage('Bot', response.response);
                        }
                    });
                }
            });

            $('#user-input').keypress(function(e) {
                if (e.which == 13) {
                    $('#send-button').click();
                }
            });
        });
    </script>
</body>
</html>

上一篇
Day28: 使用Flask在本地端架設自己的DialoGPT聊天室
下一篇
Day30: 結語,LLM的漫漫長路 (還是沒脫魯🥹
系列文
軟體工程師的脫魯日誌-持續介紹hardcore AI研究直到脫魯🥹30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言